iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0
Kubernetes

從零到一: 使用Spring Boot、Kubernetes 和 Istio實現微服務架構系列 第 10

Day 10 使用Spring Boot、Kubernetes 和 Istio實現微服務架構 - JWT

  • 分享至 

  • xImage
  •  

2. 配置 UserDetailsService 和 UserDetails

我們需要定義一個 UserDetailsService,用來加載用戶信息,並且實現 UserDetails 來封裝用戶數據。

2.1 UserDetails
UserDetails 是一個用來描述用戶資料的接口:

package com.example.demo.model;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;

public class CustomUserDetails implements UserDetails {

    private String username;
    private String password;

    public CustomUserDetails(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null; // You can return roles or authorities here
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

package com.example.demo.service;

import com.example.demo.model.CustomUserDetails;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 這裡通常會從數據庫加載用戶數據
        // 在這裡我們簡單地返回一個靜態用戶
        if ("user".equals(username)) {
            return new CustomUserDetails("user", "{noop}password");  // {noop} 用於表示密碼不加密
        } else {
            throw new UsernameNotFoundException("User not found");
        }
    }
}


驗証JWT

package com.example.demo.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtil {

    private String SECRET_KEY = "secret"; // 用來簽名的密鑰,這裡是簡單的字串,但實際應用中應保存在安全地方

    // 生成 JWT token
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username);
    }

    // 創建 JWT token
    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 小時
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    // 驗證 token
    public Boolean validateToken(String token, String username) {
        final String extractedUsername = extractUsername(token);
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }

    // 從 token 中提取 username
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    // 提取所有 Claims
    public Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    // 提取某個指定的 Claims
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    // 驗證 token 是否過期
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    // 提取 token 的過期時間
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
}

Security配置

package com.example.demo.config;

import com.example.demo.filter.JwtRequestFilter;
import com.example.demo.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import org.springframework.security.web.SecurityConfigurerAdapter;

@EnableWebSecurity
public class SecurityConfig extends SecurityConfigurerAdapter {

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance(); // 使用不加密的密碼,實際應用中應使用加密
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests().antMatchers("/authenticate").permitAll() // 允許 "/authenticate" 路徑無需驗證
                .anyRequest().authenticated()
                .and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 不創建 session
        http.addFilterBefore(jwtRequestFilter


上一篇
Day 9 - SpringBoot起手式使用Spring Boot、Kubernetes 和 Istio實現微服務架構 - MVC架構
下一篇
Day 11 使用Spring Boot、Kubernetes 和 Istio實現微服務架構 - Docker 概念
系列文
從零到一: 使用Spring Boot、Kubernetes 和 Istio實現微服務架構30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言